package cn.fanqinghong.ymcc.service.impl;

import cn.fanqinghong.ymcc.constants.AliPayConstants;
import cn.fanqinghong.ymcc.domain.AlipayInfo;
import cn.fanqinghong.ymcc.domain.PayOrder;
import cn.fanqinghong.ymcc.dto.AlipayNotifyDto;
import cn.fanqinghong.ymcc.dto.CoursePayDto;
import cn.fanqinghong.ymcc.dto.NotifyDto;
import cn.fanqinghong.ymcc.exception.GlobalException;
import cn.fanqinghong.ymcc.mapper.PayOrderMapper;
import cn.fanqinghong.ymcc.service.IAlipayInfoService;
import cn.fanqinghong.ymcc.service.IPayOrderService;
import cn.fanqinghong.ymcc.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.AlipayTradeCloseResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.alipay.easysdk.payment.page.Client;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Map;


/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author superman.fan
 * @since 2022-07-30
 */
@Service
@Slf4j
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private IAlipayInfoService alipayInfoService;
    /**
     * 通过订单号查询数据
     * @param orderNo
     * @return
     */
    @Override
    public PayOrder selectByOrderNo(String orderNo) {
        Wrapper<PayOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        return selectOne(wrapper);
    }

    /**
     * 发起支付
     * @param dto
     * @return
     */
    @Override
    public String payApply(CoursePayDto dto) {
        AssertUtil.isNotNull(dto.getOrderNo(),"订单编号不存在");
        PayOrder payOrder = selectByOrderNo(dto.getOrderNo());
        AssertUtil.isNotNull(payOrder,"支付订单不存在");
        //判断订单状态是否伟待支付
        boolean wait = payOrder.getPayStatus() == PayOrder.COURSE_ORDER_WAITING_TO_PAY;
        AssertUtil.isTrue(wait,"订单状态异常，请重新下单");
        //支付方式:0余额直接，1支付宝，2微信,3银联
        if (dto.getPayType() == 0 ){
            return null;
        }else if (dto.getPayType() == 1){//支付宝支付,返回支付Form表单

            return payAlibaba(payOrder,dto);
        }
        return null;
    }



    /**
     * 调用支付宝，返回form表单
     * @param payOrder
     * @param dto
     * @return
     */
    public String payAlibaba(PayOrder payOrder,CoursePayDto dto) {
        AlipayInfo info = alipayInfoService.selectOne(null);
        // 1. 设置参数（全局只需设置一次）
        Factory.setOptions(getOptions(info));
        try {
            //2.网页端支付
            Client page = Factory.Payment.Page();
            AlipayTradePagePayResponse response = page.pay(
                    payOrder.getSubject(),
                    payOrder.getOrderNo(),
                    payOrder.getAmount().toString(),
                    StringUtils.isEmpty(dto.getCallUrl())?info.getReturnUrl():dto.getCallUrl());//同步回调地址

            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                System.out.println("调用成功");
                return response.getBody();//Form表单
            } else {
                System.err.println("调用失败，原因：" );
                throw new GlobalException("支付失败请重新再试");
            }
        } catch (Exception e) {
            System.err.println("调用遭遇异常，原因：" + e.getMessage());
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    private Config getOptions(AlipayInfo info) {
        Config config = new Config();
        config.protocol = info.getProtocol();
        config.gatewayHost = info.getGatewayHost();//沙箱api地址
        config.signType = info.getSignType();
        config.appId = info.getAppId();
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = info.getMerchantPrivateKey();
        //注：证书文件路径支持设置为文件系统中的路径或CLASS_PATH中的路径，优先从文件系统中加载，加载失败后会继续尝试从CLASS_PATH中加载
        /*config.merchantCertPath = "<-- 请填写您的应用公钥证书文件路径，例如：/foo/appCertPublicKey_2019051064521003.crt -->";
        config.alipayCertPath = "<-- 请填写您的支付宝公钥证书文件路径，例如：/foo/alipayCertPublicKey_RSA2.crt -->";
        config.alipayRootCertPath = "<-- 请填写您的支付宝根证书文件路径，例如：/foo/alipayRootCert.crt -->";*/
        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = info.getAlipayPublicKey();
        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = info.getNotifyUrl();
        //可设置AES密钥，调用AES加解密相关接口时需要（可选）
        config.encryptKey = "";
        return config;
    }

    /**
     * 支付宝支付成功之后的异步回调逻辑
     * 修改支付状态
     * @param dto
     * @return
     */
    @Override
    public String notify(AlipayNotifyDto dto) throws Exception {
        //获得毁掉的数据，将json形式的数据转变为string
        String notifyStr = JSONObject.toJSONString(dto);
        Map<String,String> map = JSONObject.parseObject(notifyStr, Map.class);
        //接收到的所有参数放入一个Map中
        Boolean isValid = Factory.Payment.Common().verifyNotify(map);
        if (!isValid){
            return "Fail";
        }
        //通过订单号查询数据
        PayOrder payOrder = selectByOrderNo(dto.getOut_trade_no());
        //判断是否是我们的订单
        if (payOrder == null){
            return "Fail";
        }
        //如果支付订单为取消状态，退款
        if (payOrder.getPayStatus().intValue() == PayOrder.COURSE_ORDER_PAY_CANCEL){
            log.info("调用支付宝退款！！");
            try {
                AlipayTradeRefundResponse refund = Factory.Payment.Common().refund(payOrder.getOrderNo(), payOrder.getAmount().toString());
            } catch (Exception e) {
                e.printStackTrace();
                /**
                 * 做兜底
                 * 2.保存要取消支付单的记录到数据库，运维人员来人工跟进
                 * 3.给运维人员发站内信，邮件，消息---- 对接钉钉推销
                 */
            }
            return "success";
        }
        //判断是否是待支付状态
        if (payOrder.getPayStatus() != PayOrder.COURSE_ORDER_WAITING_TO_PAY){
            return "Fail";
        }
        // 回调的交易状态不是交易成功的话，返回fail
        if (!dto.getTrade_status().equals(AlipayNotifyDto.TRADE_SUCCESS) ){
            return "Fail";
        }
        //验证交易成功之后对订单状态的修改====
        //1.修改支付表的订单状态
        payOrder.setPayStatus(PayOrder.COURSE_ORDER_PAY_SUCCESS);//设置订单状态伟成功
        payOrder.setUpdateTime(new Date());
        //@TODO 保存支付流水
        //异步发送消息给生产者
        //消息体需要 订单号  购买人  课程id  PayOrder表中的额外参数就封装了购买人和课程id
        NotifyDto notifyDto = new NotifyDto();
        notifyDto.setExtParams(payOrder.getExtParams());
        notifyDto.setOrderNo(payOrder.getOrderNo());
        //将数据转为json格式的数据封装到message中
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(notifyDto)).build();
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
                AliPayConstants.ALI_ORDER_NOTIFY_GROUP,
                AliPayConstants.ALI_ORDER_NOTIFY_TOPIC_TAG,
                message,
                payOrder
        );
        //得到支付宝支付状态
        LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();
        SendStatus sendStatus = transactionSendResult.getSendStatus();
        if(localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus != SendStatus.SEND_OK){
                log.info("检查发送消息的状态");
                throw new GlobalException("处理支付结果失败");
        }
        return "success";
    }

    /**
     * 超时取消支付订单
     * @param orderNo
     */
    @Override
    public void cancelOrder(String orderNo) {
        AssertUtil.isNotNull(orderNo,"修改订单失败");
        PayOrder payOrder = selectByOrderNo(orderNo);
        AssertUtil.isNotNull(payOrder,"订单有误");
        //如果订单状态为待支付
        if (payOrder.getPayStatus().intValue() == PayOrder.COURSE_ORDER_WAITING_TO_PAY){
            payOrder.setPayStatus(PayOrder.COURSE_ORDER_PAY_CANCEL);
            updateById(payOrder);
        }
        //调用支付宝，关闭支付
        try {
            AlipayTradeCloseResponse close = Factory.Payment.Common().close(orderNo);

        }catch (Exception e){
            e.printStackTrace();
            /**
             * 做兜底
             * 1.重试3次
             * 2.保存要取消支付单的记录到数据库，运维人员来人工跟进
             * 3.给运维人员发站内信，邮件，消息---- 对接钉钉推销
             */
        }

    }
}
